home *** CD-ROM | disk | FTP | other *** search
/ Mac Easy 2010 May / Mac Life Ubuntu.iso / casper / filesystem.squashfs / usr / lib / perl5 / Net / DBus / Reactor.pm < prev    next >
Encoding:
Perl POD Document  |  2008-02-20  |  17.6 KB  |  779 lines

  1. # -*- perl -*-
  2. #
  3. # Copyright (C) 2004-2006 Daniel P. Berrange
  4. #
  5. # This program is free software; You can redistribute it and/or modify
  6. # it under the same terms as Perl itself. Either:
  7. #
  8. # a) the GNU General Public License as published by the Free
  9. #   Software Foundation; either version 2, or (at your option) any
  10. #   later version,
  11. #
  12. # or
  13. #
  14. # b) the "Artistic License"
  15. #
  16. # The file "COPYING" distributed along with this file provides full
  17. # details of the terms and conditions of the two licenses.
  18.  
  19. =pod
  20.  
  21. =head1 NAME
  22.  
  23. Net::DBus::Reactor - application event loop
  24.  
  25. =head1 SYNOPSIS
  26.  
  27. Create and run an event loop:
  28.  
  29.    use Net::DBus::Reactor;
  30.    my $reactor = Net::DBus::Reactor->main();
  31.  
  32.    $reactor->run();
  33.  
  34. Manage some file handlers
  35.  
  36.    $reactor->add_read($fd,
  37.                       Net::DBus::Callback->new(method => sub {
  38.                          my $fd = shift;
  39.                          ...read some data...
  40.                       }, args => [$fd]);
  41.  
  42.    $reactor->add_write($fd,
  43.                        Net::DBus::Callback->new(method => sub {
  44.                           my $fd = shift;
  45.                           ...write some data...
  46.                        }, args => [$fd]);
  47.  
  48. Temporarily (dis|en)able a handle
  49.  
  50.    # Disable
  51.    $reactor->toggle_read($fd, 0);
  52.    # Enable
  53.    $reactor->toggle_read($fd, 1);
  54.  
  55. Permanently remove a handle
  56.  
  57.    $reactor->remove_read($fd);
  58.  
  59. Manage a regular timeout every 100 milliseconds
  60.  
  61.    my $timer = $reactor->add_timeout(100,
  62.                                      Net::DBus::Callback->new(
  63.               method => sub {
  64.                  ...process the alarm...
  65.               }));
  66.  
  67. Temporarily (dis|en)able a timer
  68.  
  69.    # Disable
  70.    $reactor->toggle_timeout($timer, 0);
  71.    # Enable
  72.    $reactor->toggle_timeout($timer, 1);
  73.  
  74. Permanently remove a timer
  75.  
  76.    $reactor->remove_timeout($timer);
  77.  
  78. Add a post-dispatch hook
  79.  
  80.    my $hook = $reactor->add_hook(Net::DBus::Callback->new(
  81.          method => sub {
  82.             ... do some work...
  83.          }));
  84.  
  85. Remove a hook
  86.  
  87.    $reactor->remove_hook($hook);
  88.  
  89. =head1 DESCRIPTION
  90.  
  91. This class provides a general purpose event loop for
  92. the purposes of multiplexing I/O events and timeouts
  93. in a single process. The underlying implementation is
  94. done using the select system call. File handles can
  95. be registered for monitoring on read, write and exception
  96. (out-of-band data) events. Timers can be registered
  97. to expire with a periodic frequency. These are implemented
  98. using the timeout parameter of the select system call.
  99. Since this parameter merely represents an upper bound
  100. on the amount of time the select system call is allowed
  101. to sleep, the actual period of the timers may vary. Under
  102. normal load this variance is typically 10 milliseconds.
  103. Finally, hooks may be registered which will be invoked on
  104. each iteration of the event loop (ie after processing
  105. the file events, or timeouts indicated by the select
  106. system call returning).
  107.  
  108. =head1 METHODS
  109.  
  110. =over 4
  111.  
  112. =cut
  113.  
  114. package Net::DBus::Reactor;
  115.  
  116. use 5.006;
  117. use strict;
  118. use warnings;
  119.  
  120. use Net::DBus::Binding::Watch;
  121. use Net::DBus::Callback;
  122. use Time::HiRes qw(gettimeofday);
  123.  
  124. =item my $reactor = Net::DBus::Reactor->new();
  125.  
  126. Creates a new event loop ready for monitoring file handles, or 
  127. generating timeouts. Except in very unsual circumstances (examples
  128. of which I can't think up) it is not neccessary or desriable to
  129. explicitly create new reactor instances. Instead call the L<main>
  130. method to get a handle to the singleton instance.
  131.  
  132. =cut
  133.  
  134. sub new {
  135.     my $proto = shift;
  136.     my $class = ref($proto) || $proto;
  137.     my %params = @_;
  138.     my $self = {};
  139.  
  140.     $self->{fds} = {
  141.     read => {},
  142.     write => {},
  143.     exception => {}
  144.     };
  145.     $self->{timeouts} = [];
  146.     $self->{hooks} = [];
  147.  
  148.     bless $self, $class;
  149.  
  150.     return $self;
  151. }
  152.  
  153. use vars qw($main_reactor);
  154.  
  155. =item $reactor = Net::DBus::Reactor->main;
  156.  
  157. Return a handle to the singleton instance of the reactor. This
  158. is the recommended way of getting hold of a reactor, since it
  159. removes the need for modules to pass around handles to their
  160. privately created reactors.
  161.  
  162. =cut
  163.  
  164. sub main {
  165.     my $class = shift;
  166.     $main_reactor = $class->new() unless defined $main_reactor;
  167.     return $main_reactor;
  168. }
  169.  
  170.  
  171. =item $reactor->manage($connection);
  172.  
  173. =item $reactor->manage($server);
  174.  
  175. Registers a C<Net::DBus::Connection> or C<Net::DBus::Server> object
  176. for management by the event loop. This basically involves
  177. hooking up the watch & timeout callbacks to the event loop.
  178. For connections it will also register a hook to invoke the
  179. C<dispatch> method periodically.
  180.  
  181. =cut
  182.  
  183. sub manage {
  184.     my $self = shift;
  185.     my $object = shift;
  186.  
  187.     if ($object->can("set_watch_callbacks")) {
  188.     $object->set_watch_callbacks(sub {
  189.         my $object = shift;
  190.         my $watch = shift;
  191.  
  192.         $self->_manage_watch_on($object, $watch);
  193.     }, sub {
  194.         my $object = shift;
  195.         my $watch = shift;
  196.  
  197.         $self->_manage_watch_off($object, $watch);
  198.     }, sub {
  199.         my $object = shift;
  200.         my $watch = shift;
  201.  
  202.         $self->_manage_watch_toggle($object, $watch);
  203.     });
  204.     }
  205.     
  206.     if ($object->can("set_timeout_callbacks")) {
  207.     $object->set_timeout_callbacks(sub {
  208.         my $object = shift;
  209.         my $timeout = shift;
  210.         
  211.         my $key = $self->add_timeout($timeout->get_interval,
  212.                      Net::DBus::Callback->new(object => $timeout,
  213.                                   method => "handle",
  214.                                   args => []),
  215.                      $timeout->is_enabled);
  216.         $timeout->set_data($key);
  217.     }, sub {
  218.         my $object = shift;
  219.         my $timeout = shift;
  220.         
  221.         my $key = $timeout->get_data;
  222.         $self->remove_timeout($key);
  223.     }, sub {
  224.         my $object = shift;
  225.         my $timeout = shift;
  226.         
  227.         my $key = $timeout->get_data;
  228.         $self->remove_timeout($key,
  229.                   $timeout->is_enabled,
  230.                   $timeout->get_interval);
  231.     });
  232.     }
  233.     
  234.     if ($object->can("dispatch")) {
  235.     $self->add_hook(Net::DBus::Callback->new(object => $object, 
  236.                          method => "dispatch", 
  237.                          args => []), 
  238.             1);
  239.     }
  240.     if ($object->can("flush")) {
  241.     $self->add_hook(Net::DBus::Callback->new(object => $object, 
  242.                          method => "flush", 
  243.                          args => []), 
  244.             1);
  245.     }
  246. }
  247.  
  248.  
  249. sub _manage_watch_on {
  250.     my $self = shift;
  251.     my $object = shift;
  252.     my $watch = shift;
  253.     my $flags = $watch->get_flags;
  254.  
  255.     if ($flags & &Net::DBus::Binding::Watch::READABLE) {
  256.     $self->add_read($watch->get_fileno, 
  257.             Net::DBus::Callback->new(object => $watch, 
  258.                         method => "handle", 
  259.                         args => [&Net::DBus::Binding::Watch::READABLE]), 
  260.             $watch->is_enabled);
  261.     }
  262.     if ($flags & &Net::DBus::Binding::Watch::WRITABLE) {
  263.     $self->add_write($watch->get_fileno, 
  264.              Net::DBus::Callback->new(object => $watch, 
  265.                          method => "handle", 
  266.                          args => [&Net::DBus::Binding::Watch::WRITABLE]), 
  267.              $watch->is_enabled);
  268.     }
  269. #    $self->add_exception($watch->get_fileno, $watch, 
  270. #             Net::DBus::Callback->new(object => $watch, 
  271. #                         method => "handle", 
  272. #                         args => [&Net::DBus::Binding::Watch::ERROR]), 
  273. #             $watch->is_enabled);
  274.     
  275. }
  276.  
  277. sub _manage_watch_off {
  278.     my $self = shift;
  279.     my $object = shift;
  280.     my $watch = shift;
  281.     my $flags = $watch->get_flags;
  282.     
  283.     if ($flags & &Net::DBus::Binding::Watch::READABLE) {
  284.     $self->remove_read($watch->get_fileno);
  285.     }
  286.     if ($flags & &Net::DBus::Binding::Watch::WRITABLE) {
  287.     $self->remove_write($watch->get_fileno);
  288.     }
  289. #    $self->remove_exception($watch->get_fileno);
  290. }
  291.  
  292. sub _manage_watch_toggle {
  293.     my $self = shift;
  294.     my $object = shift;
  295.     my $watch = shift;
  296.     my $flags = $watch->get_flags;
  297.     
  298.     if ($flags & &Net::DBus::Binding::Watch::READABLE) {
  299.     $self->toggle_read($watch->get_fileno, $watch->is_enabled);
  300.     }
  301.     if ($flags & &Net::DBus::Binding::Watch::WRITABLE) {
  302.     $self->toggle_write($watch->get_fileno, $watch->is_enabled);
  303.     }
  304.     $self->toggle_exception($watch->get_fileno, $watch->is_enabled);
  305. }
  306.  
  307.  
  308. =item $reactor->run();
  309.  
  310. Starts the event loop monitoring any registered
  311. file handles and timeouts. At least one file
  312. handle, or timer must have been registered prior
  313. to running the reactor, otherwise it will immediately
  314. exit. The reactor will run until all registered
  315. file handles, or timeouts have been removed, or
  316. disabled. The reactor can be explicitly stopped by
  317. calling the C<shutdown> method.
  318.  
  319. =cut
  320.  
  321. sub run {
  322.     my $self = shift;
  323.  
  324.     $self->{running} = 1;
  325.     while ($self->{running}) { $self->step };
  326. }
  327.  
  328. =item $reactor->shutdown();
  329.  
  330. Explicitly shutdown the reactor after pending
  331. events have been processed.
  332.  
  333. =cut
  334.  
  335. sub shutdown {
  336.     my $self = shift;
  337.     $self->{running} = 0;
  338. }
  339.  
  340. =item $reactor->step();
  341.  
  342. Perform one iteration of the event loop, going to
  343. sleep until an event occurs on a registered file
  344. handle, or a timeout occurrs. This method is generally
  345. not required in day-to-day use.
  346.  
  347. =cut
  348.  
  349. sub step {
  350.     my $self = shift;
  351.    
  352.     my @callbacks = $self->_dispatch_hook();
  353.     
  354.     foreach my $callback (@callbacks) {
  355.     $callback->invoke;
  356.     }
  357.  
  358.     my ($ri, $ric) = $self->_bits("read");
  359.     my ($wi, $wic) = $self->_bits("write");
  360.     my ($ei, $eic) = $self->_bits("exception");
  361.     my $timeout = $self->_timeout($self->_now);
  362.     
  363.     if (!$ric && !$wic && !$eic && !(defined $timeout)) {
  364.     $self->{running} = 0;
  365.     return;
  366.     }
  367.         
  368.     my ($ro, $wo, $eo);
  369.     my $n = select($ro=$ri,$wo=$wi,$eo=$ei, (defined $timeout ? ($timeout ? $timeout/1000 : 0) : undef));
  370.     
  371.     @callbacks = ();
  372.     if ($n) {
  373.     push @callbacks, $self->_dispatch_fd("read", $ro);
  374.     push @callbacks, $self->_dispatch_fd("write", $wo);
  375.     push @callbacks, $self->_dispatch_fd("error", $eo);
  376.     }
  377.     push @callbacks, $self->_dispatch_timeout($self->_now);
  378.     #push @callbacks, $self->_dispatch_hook();
  379.     
  380.     foreach my $callback (@callbacks) {
  381.     $callback->invoke;
  382.     }
  383.  
  384.     return 1;
  385. }
  386.  
  387. sub _now {
  388.     my $self = shift;
  389.     
  390.     my @now = gettimeofday;
  391.     
  392.     return $now[0] * 1000 + (($now[1] - ($now[1] % 1000)) / 1000);
  393. }
  394.  
  395. sub _bits {
  396.     my $self = shift;
  397.     my $type = shift;
  398.     my $vec = '';
  399.  
  400.     my $count = 0;
  401.     foreach (keys %{$self->{fds}->{$type}}) {
  402.     next unless $self->{fds}->{$type}->{$_}->{enabled};
  403.  
  404.     $count++;
  405.     vec($vec, $_, 1) = 1;
  406.     }
  407.     return ($vec, $count);
  408. }
  409.  
  410. sub _timeout {
  411.     my $self = shift;
  412.     my $now = shift;
  413.     
  414.     my $timeout;
  415.     foreach (@{$self->{timeouts}}) {
  416.     next unless $_->{enabled};
  417.  
  418.     my $expired = $now - $_->{last_fired};
  419.     my $interval = ($expired > $_->{interval} ? 0 : $_->{interval} - $expired);
  420.     $timeout = $interval if !(defined $timeout) ||
  421.         ($interval < $timeout);
  422.     }
  423.     return $timeout;
  424. }
  425.  
  426.  
  427. sub _dispatch_fd {
  428.     my $self = shift;
  429.     my $type = shift;
  430.     my $vec = shift;
  431.     
  432.     my @callbacks;
  433.     foreach my $fd (keys %{$self->{fds}->{$type}}) {
  434.     next unless $self->{fds}->{$type}->{$fd}->{enabled};
  435.  
  436.     if (vec($vec, $fd, 1)) {
  437.         my $rec = $self->{fds}->{$type}->{$fd};
  438.         
  439.         push @callbacks, $self->{fds}->{$type}->{$fd}->{callback};
  440.     }
  441.     }
  442.     return @callbacks;
  443. }
  444.  
  445.  
  446. sub _dispatch_timeout {
  447.     my $self = shift;
  448.     my $now = shift;
  449.     
  450.     my @callbacks;
  451.     foreach my $timeout (@{$self->{timeouts}}) {
  452.     next unless $timeout->{enabled};
  453.     my $expired = $now - $timeout->{last_fired};
  454.  
  455.     # Select typically returns a little (0-10 ms) before we 
  456.     # asked it for. (8 milliseconds seems reasonable balance
  457.     # between early timeouts & extra select calls
  458.     if ($expired >= ($timeout->{interval}-8)) {
  459.         $timeout->{last_fired} = $now;
  460.         push @callbacks, $timeout->{callback};
  461.     }
  462.     }
  463.     return @callbacks;
  464. }
  465.  
  466.  
  467. sub _dispatch_hook {
  468.     my $self = shift;
  469.     my $now = shift;
  470.  
  471.     my @callbacks;
  472.     foreach my $hook (@{$self->{hooks}}) {
  473.     next unless $hook->{enabled};
  474.     push @callbacks, $hook->{callback};
  475.     }
  476.     return @callbacks;
  477. }
  478.  
  479.  
  480. =item $reactor->add_read($fd, $callback[, $status]);
  481.  
  482. Registers a file handle for monitoring of read
  483. events. The C<$callback> parameter specifies an
  484. instance of the C<Net::DBus::Callback> object to invoke
  485. each time an event occurs. The optional C<$status>
  486. parameter is a boolean value to specify whether the
  487. watch is initially enabled.
  488.  
  489. =cut
  490.  
  491. sub add_read {
  492.     my $self = shift;
  493.     $self->_add("read", @_);
  494. }
  495.  
  496. =item $reactor->add_write($fd, $callback[, $status]);
  497.  
  498. Registers a file handle for monitoring of write
  499. events. The C<$callback> parameter specifies an
  500. instance of the C<Net::DBus::Callback> object to invoke
  501. each time an event occurs. The optional C<$status>
  502. parameter is a boolean value to specify whether the
  503. watch is initially enabled.
  504.  
  505. =cut
  506.  
  507. sub add_write {
  508.     my $self = shift;
  509.     $self->_add("write", @_);
  510. }
  511.  
  512.  
  513. =item $reactor->add_exception($fd, $callback[, $status]);
  514.  
  515. Registers a file handle for monitoring of exception
  516. events. The C<$callback> parameter specifies an
  517. instance of the C<Net::DBus::Callback> object to invoke
  518. each time an event occurs. The optional C<$status>
  519. parameter is a boolean value to specify whether the
  520. watch is initially enabled.
  521.  
  522. =cut
  523.  
  524. sub add_exception {
  525.     my $self = shift;
  526.     $self->_add("exception", @_);
  527. }
  528.  
  529.  
  530. =item my $id = $reactor->add_timeout($interval, $callback, $status);
  531.  
  532. Registers a new timeout to expire every C<$interval>
  533. milliseconds. The C<$callback> parameter specifies an
  534. instance of the C<Net::DBus::Callback> object to invoke
  535. each time the timeout expires. The optional C<$status>
  536. parameter is a boolean value to specify whether the
  537. timeout is initially enabled. The return parameter is
  538. a unique identifier which can be used to later remove
  539. or disable the timeout.
  540.  
  541. =cut
  542.  
  543. sub add_timeout {
  544.     my $self = shift;
  545.     my $interval = shift;
  546.     my $callback = shift;
  547.     my $enabled = shift;    
  548.     $enabled = 1 unless defined $enabled;
  549.  
  550.     my $key;
  551.     for (my $i = 0 ; $i <= $#{$self->{timeouts}} && !(defined $key); $i++) {
  552.     $key = $i unless defined $self->{timeouts}->[$i];
  553.     }
  554.     $key = $#{$self->{timeouts}}+1 unless defined $key;
  555.     
  556.     $self->{timeouts}->[$key] = {
  557.     interval => $interval,
  558.     last_fired => $self->_now,
  559.     callback => $callback,
  560.     enabled => $enabled
  561.     };
  562.  
  563.     return $key;
  564. }
  565.  
  566.  
  567. =item $reactor->remove_timeout($id);
  568.  
  569. Removes a previously registered timeout specified by
  570. the C<$id> parameter.
  571.  
  572. =cut
  573.  
  574. sub remove_timeout {
  575.     my $self = shift;
  576.     my $key = shift;
  577.     
  578.     die "no timeout active with key '$key'" 
  579.     unless defined $self->{timeouts}->[$key];
  580.  
  581.     $self->{timeouts}->[$key] = undef;
  582. }
  583.  
  584.  
  585. =item $reactor->toggle_timeout($id, $status[, $interval]);
  586.  
  587. Updates the state of a previously registered timeout
  588. specifed by the C<$id> parameter. The C<$status>
  589. parameter specifies whether the timeout is to be enabled
  590. or disabled, while the optional C<$interval> parameter
  591. can be used to change the period of the timeout.
  592.  
  593. =cut
  594.  
  595. sub toggle_timeout {
  596.     my $self = shift;
  597.     my $key = shift;
  598.     my $enabled = shift;
  599.     
  600.     $self->{timeouts}->[$key]->{enabled} = $enabled;
  601.     $self->{timeouts}->[$key]->{interval} = shift if @_;
  602. }
  603.  
  604.  
  605. =item my $id = $reactor->add_hook($callback[, $status]);
  606.  
  607. Registers a new hook to be fired on each iteration
  608. of the event loop. The C<$callback> parameter
  609. specifies an instance of the C<Net::DBus::Callback>
  610. class to invoke. The C<$status> parameter determines
  611. whether the hook is initially enabled, or disabled.
  612. The return parameter is a unique id which should
  613. be used to later remove, or disable the hook.
  614.  
  615. =cut
  616.  
  617. sub add_hook {
  618.     my $self = shift;
  619.     my $callback = shift;
  620.     my $enabled = shift;    
  621.     $enabled = 1 unless defined $enabled;
  622.     
  623.     my $key;
  624.     for (my $i = 0 ; $i <= $#{$self->{hooks}} && !(defined $key); $i++) {
  625.     $key = $i unless defined $self->{hooks}->[$i];
  626.     }
  627.     $key = $#{$self->{hooks}}+1 unless defined $key;
  628.  
  629.     $self->{hooks}->[$key] = {
  630.     callback => $callback,
  631.     enabled => $enabled
  632.     };
  633.  
  634.     return $key;
  635. }
  636.  
  637.  
  638. =item $reactor->remove_hook($id)
  639.  
  640. Removes the previously registered hook identified
  641. by C<$id>.
  642.  
  643. =cut
  644.  
  645. sub remove_hook {
  646.     my $self = shift;
  647.     my $key = shift;
  648.     
  649.     die "no hook present with key '$key'" 
  650.     unless defined $self->{hooks}->[$key];
  651.  
  652.  
  653.     $self->{hooks}->[$key] = undef;
  654. }
  655.  
  656. =item $reactor->toggle_hook($id[, $status])
  657.  
  658. Updates the status of the previously registered
  659. hook identified by C<$id>. The C<$status> parameter
  660. determines whether the hook is to be enabled or
  661. disabled.
  662.  
  663. =cut
  664.  
  665. sub toggle_hook {
  666.     my $self = shift;
  667.     my $key = shift;
  668.     my $enabled = shift;
  669.     
  670.     $self->{hooks}->[$key]->{enabled} = $enabled;
  671. }
  672.  
  673. sub _add {
  674.     my $self = shift;
  675.     my $type = shift;
  676.     my $fd = shift;
  677.     my $callback = shift;
  678.     my $enabled = shift;
  679.     $enabled = 1 unless defined $enabled;
  680.     
  681.     $self->{fds}->{$type}->{$fd} = {
  682.     callback => $callback,
  683.     enabled => $enabled
  684.     };
  685. }
  686.  
  687. =item $reactor->remove_read($fd);
  688.  
  689. =item $reactor->remove_write($fd);
  690.  
  691. =item $reactor->remove_exception($fd);
  692.  
  693. Removes a watch on the file handle C<$fd>.
  694.  
  695. =cut
  696.  
  697. sub remove_read {
  698.     my $self = shift;
  699.     $self->_remove("read", @_);
  700. }
  701.  
  702. sub remove_write {
  703.     my $self = shift;
  704.     $self->_remove("write", @_);
  705. }
  706.  
  707. sub remove_exception {
  708.     my $self = shift;
  709.     $self->_remove("exception", @_);
  710. }
  711.  
  712. sub _remove {
  713.     my $self = shift;
  714.     my $type = shift;
  715.     my $fd = shift;
  716.  
  717.     die "no handle ($type) active with fd '$fd'" 
  718.     unless exists $self->{fds}->{$type}->{$fd};
  719.  
  720.     delete $self->{fds}->{$type}->{$fd};
  721. }
  722.  
  723. =item $reactor->toggle_read($fd, $status);
  724.  
  725. =item $reactor->toggle_write($fd, $status);
  726.  
  727. =item $reactor->toggle_exception($fd, $status);
  728.  
  729. Updates the status of a watch on the file handle C<$fd>.
  730. The C<$status> parameter species whether the watch is
  731. to be enabled or disabled.
  732.  
  733. =cut
  734.  
  735. sub toggle_read {
  736.     my $self = shift;
  737.     $self->_toggle("read", @_);
  738. }
  739.  
  740. sub toggle_write {
  741.     my $self = shift;
  742.     $self->_toggle("write", @_);
  743. }
  744.  
  745. sub toggle_exception {
  746.     my $self = shift;
  747.     $self->_toggle("exception", @_);
  748. }
  749.  
  750. sub _toggle {
  751.     my $self = shift;
  752.     my $type = shift;
  753.     my $fd = shift;
  754.     my $enabled = shift;
  755.  
  756.     $self->{fds}->{$type}->{$fd}->{enabled} = $enabled;
  757. }
  758.  
  759.  
  760. 1;
  761.  
  762. =pod
  763.  
  764. =back
  765.  
  766. =head1 SEE ALSO
  767.  
  768. L<Net::DBus::Callback>, L<Net::DBus::Connection>, L<Net::DBus::Server>
  769.  
  770. =head1 AUTHOR
  771.  
  772. Daniel Berrange E<lt>dan@berrange.comE<gt>
  773.  
  774. =head1 COPYRIGHT
  775.  
  776. Copyright 2004 by Daniel Berrange
  777.  
  778. =cut
  779.